package problems.multiplexer;

import java.io.File;
import java.util.Arrays;

import ec.ECFitnessEvaluator;
import ec.Solution;
import ge.BNFGrammarParser;
import ge.ExpressionEvaluator;

public class MultiplexerFitnessEvaluator extends ECFitnessEvaluator {
	private Boolean[][] all_inputs;
	private Boolean[] all_targets;
	Class<Boolean> return_type = Boolean.class;

	int num_select_bits = 2;

	int num_bits = 0;

	String[] parameter_names = null;

	Class<?>[] parameter_types = null;

	public MultiplexerFitnessEvaluator(int num_select_bits, String grammar_file) {
		this.num_select_bits = num_select_bits;
		num_bits = num_select_bits + ((int) Math.pow(2, num_select_bits));
		parameter_names = new String[num_bits];
		parameter_types = new Class<?>[num_bits];

		BNFGrammarParser parser = new BNFGrammarParser(new File(
				grammar_file),
				"problems.multiplexer.Symbols$");

		for (int i = 0; i < num_bits; i++) {
			String prefix = "s";
			int idx = i + 1;
			if (i >= num_select_bits) {
				prefix = "i";
				idx -= num_select_bits;
			}
			parameter_names[i] = prefix + idx;
			parameter_types[i] = boolean.class;
		}

		makeTables(num_select_bits);
	}

	private void makeTables(int num_select_bits) {
		int num_bits = num_select_bits + ((int) Math.pow(2, num_select_bits));
		all_inputs = new Boolean[(int) Math.pow(2, num_bits)][];
		all_targets = new Boolean[(int) Math.pow(2, num_bits)];

		Boolean[] inputs = new Boolean[num_bits];
		Arrays.fill(inputs, false);
		for (int i = 0; i < Math.pow(2, inputs.length); i++) {
			boolean carry = true;
			for (int j = num_bits - 1; j >= 0 && carry; j--) {
				if (carry) {
					carry = inputs[j];
					inputs[j] = !inputs[j];
				}
			}

			all_inputs[i] = new Boolean[num_bits];
			System.arraycopy(inputs, 0, all_inputs[i], 0, num_bits);

			// get the value of the select bits
			int selection = 0;
			for (int idx = 0; idx < num_select_bits; idx++) {
				selection += (inputs[idx] ? (int) Math.pow(2, idx) : 0);
			}

			// get the selected value
			all_targets[i] = inputs[num_select_bits + selection];

		}

	}

	public double getFitness(Solution solution) {
		String code = getCode(solution);
		ExpressionEvaluator ee = null;
		try {
			ee = new ExpressionEvaluator(code, return_type, parameter_names,
					parameter_types);
		} catch (Exception e) {
			e.printStackTrace();
		}

		double fitness = 0;
		for (int i = 0; i < all_inputs.length; i++) {
			boolean solution_output = false;
			try {
				solution_output = (Boolean) ee.execute(all_inputs[i]);
			} catch (Exception e) {
			}

			if (solution_output != all_targets[i]) {
				fitness -= 1;
			}

		}

		return fitness;
	}

	public double getMaxFitness() {
		return 0;
	}
}
